package com.three.mahjong.base.model;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import com.three.api.protocol.Packet;
import com.three.api.push.PushSender;
import com.three.mahjong.base.comm.CardTable;
import com.three.mahjong.base.constants.MJBaseDef.MJStatus;
import com.three.cache.redis.manager.RedisManager;
import com.three.config.LanguageConfig;
import com.three.constant.LanguageId;
import com.three.constant.RedisKeyConstants;
import com.three.exception.GameException;
import com.three.protocol.CommandEnum;
import com.three.protocol.HallModule;
import com.three.protocol.MJModule;
import com.three.protocol.MJModule.OptionsType;
import com.three.utils.LogUtils;


/**
 * 麻将牌桌对象
 */

public class MJTable extends CardTable {
    
    /**
     * 当前牌桌最后打出的那张牌
     */
    private int lastOutPai;
    
    /**
     * 牌桌最后摸到的牌
     */
    private int lastMoPai;
    
    /**
     * 最后那打出那张牌的玩家座位
     */
    private int lastSeat;
    
    /**
     * 下一操作者座位
     */
    private int nextSeat;
    
    /**
     * 牌局状态
     */
    private MJStatus status;
    
    /**
     * 牌局当前动作
     */
    private int action;
    
    /**
     * 庄家座位
     */
    private int bankerSeat;
    
    /**
     * 第一个色子数
     */
    private int diceOneNum;
    
    /**
     * 第二个色子数
     */
    private int diceTwoNum;
    
    /**
     * 牌桌操作时间1
     */
    private long actionTime1;
    
    /**
     * 牌桌操作时间2
     */
    private long actionTime2;
    
    /**
     * 牌桌上的牌
     */
    private List<Integer> tablePais = new ArrayList<>();
    
    /**
     * 玩法类型
     */
    private List<Integer> playTypeList = new ArrayList<>();
    
    /**
     * 加番类型
     */
    private List<Integer> extraFanList = new ArrayList<>();
    
    /**
     * 牌局玩家信息列表
     * @return
     */
    private ConcurrentHashMap<Long,MJPlayer> players = new ConcurrentHashMap<>();
    
    /**
     * 牌局座位号对应玩家id
     */
    private ConcurrentHashMap<Integer, Long> seatMap = new ConcurrentHashMap<>();

    /**
     * 玩家的可操作列表（注：多人同时操作的时候） -座位对应操作列表
     */
    private ConcurrentHashMap<Integer, List<OptionsType>> canOperateMap = new ConcurrentHashMap<>();

    /**
     * 玩家已选择的操作,座位对应操作
     */
    private ConcurrentHashMap<Integer, OptionsType> operateMap = new ConcurrentHashMap<>();

    /**
     * 投票相关
     */
    private Map<Integer, VoteInfo> voteSeatMap = new HashMap<>();
    /**
     * 投票结束时间
     */
    private long voteEndTime;

    /**
     * 支付类型
     */
    private PayType payType;
    /**
     * 底分
     */
    private int baseScore;
    /**
     * 封顶分数
     */
    private int ceilingScore;

    /**
     * 构造方法
     */
    public MJTable(){
        
    }

    private void resetVote() {
        if(System.currentTimeMillis() > voteEndTime) {
            voteEndTime = 0;
            voteSeatMap.clear();
        }
        if(voteEndTime == 0 && !voteSeatMap.isEmpty()) {
            voteSeatMap.clear();
        }
    }

    public int getCurrentMemberCount() {
        return seatMap.size();
    }

    class VoteInfo {
        private final int seatId;
        private final boolean agree;// 投票结果

        VoteInfo(int seatId, boolean agree) {
            this.seatId = seatId;
            this.agree = agree;
        }
    }

    public static MJTable build(long roomId, MJGameType gameType, RoomType roomType, PayType payType, int maxRounds, int baseScore, int ceilingScore, List<Integer> playTypeList) {
        MJTable mjTable = new MJTable();
        mjTable.roomId = roomId;
        mjTable.gameType = gameType;
        mjTable.roomType = roomType;
        mjTable.payType = payType;
        mjTable.maxRounds = maxRounds;
        mjTable.baseScore = baseScore;
        mjTable.ceilingScore = ceilingScore;
        mjTable.rounds = 1;
        mjTable.createTime = System.currentTimeMillis();
        mjTable.status = MJStatus.PREPARE;
        mjTable.playTypeList = playTypeList;
        return mjTable;
    }

    /**
     * 离开房间的类型
     * @return
     */
    public HallModule.LeaveRoomType getLeaveRoomType() {
        if(status == MJStatus.VOTE_DISSOLVED)
            return HallModule.LeaveRoomType.VOTE_DISSOLVED;
        // 游戏结束
        if(status == MJStatus.OVERGAME)
            return HallModule.LeaveRoomType.ROOM_END;
        // 强制退出
        return HallModule.LeaveRoomType.FORCED_EXIT;
    }

    /**
     * 处理投票
     * @param playId
     * @param agree
     * @throws GameException
     */
    public void dealVote(long playId, boolean agree) throws GameException {
        MJPlayer mjPlayer = players.get(playId);
        if(mjPlayer == null)
            return;
        // 重置
        resetVote();
        if(voteSeatMap.containsKey(mjPlayer.getSeat()))
            throw new GameException(LanguageConfig.getText(LanguageId.YOU_HAD_DISSOLVED));
        if(voteEndTime == 0)
            throw new GameException(LanguageConfig.getText(LanguageId.NOT_MEMBER_VOTE_TO_DISSOLVED));
        // 发起投票
        voteSeatMap.putIfAbsent(mjPlayer.getSeat(), new VoteInfo(mjPlayer.getSeat(), agree));

        // 根据投票处理解散房间
        dissolvedRoom();

        // 广播发起投票
        List<Long> playerIds = new ArrayList<>();
        for(Long playerId : seatMap.values()) {
            playerIds.add(playerId);
        }
        HallModule.NotifyVoteInfo_1011.Builder builder = HallModule.NotifyVoteInfo_1011.newBuilder();
        builder.setSeatId(mjPlayer.getSeat()).setIsAgree(agree);
        PushSender sender = PushSender.get();
        sender.send(Packet.build(CommandEnum.Command.NOTIFY_VOTE_INFO, builder.build().toByteArray()), playerIds);
    }

    public boolean isHadVoteDissolved() {
        int count = 0;
        for(VoteInfo voteInfo : voteSeatMap.values()) {
            if(voteInfo.agree)
                ++count;
        }
        return count > 4 / 2;
    }

    /**
     * 房主解散房间逻辑
     */
    private void dissolvedRoom() {
        int count = 0;
        for(VoteInfo voteInfo : voteSeatMap.values()) {
            if(voteInfo.agree)
                ++count;
        }
        if(count <= 4 / 2)
            return;

        // 解散
        status = MJStatus.VOTE_DISSOLVED;
        List<Long> playerIds = new ArrayList<>();
        for(Long playerId : seatMap.values()) {
            //  玩家退出房间
            RedisManager.I.del(RedisKeyConstants.CHESS_HALL_PLAYER_INFO + playerId);
            if(playerId != roomOwnerId) {
                playerIds.add(playerId);
            }
        }

        // 通知其他成员解散
        PushSender sender = PushSender.get();
        HallModule.DissolvedRoomResp_1007.Builder builder = HallModule.DissolvedRoomResp_1007.newBuilder();
        sender.send(Packet.build(CommandEnum.Command.DISSOLVED_ROOM, builder.build().toByteArray()), playerIds);
    }

    /**
     * 玩家离开
     * @param playId
     */
    public void memberLeave(long playId) {
        if(!players.contains(playId)) {
            LogUtils.Console.error("Member Leave MJTable Fail.Not Exist Player Id:{}", playId);
            return;
        }
        MJPlayer mjPlayer = players.remove(playId);

        // 广播成员离开的消息
        PushSender sender = PushSender.get();
        List<Long> playerIds = new ArrayList<>();
        HallModule.NotifyMemberLeaveRoom_1006.Builder builder = HallModule.NotifyMemberLeaveRoom_1006.newBuilder();
        builder.setLeaveRoomType(getLeaveRoomType());
        builder.setSeatId(mjPlayer.getSeat());
        for(Long playerId : seatMap.values()) {
            if(playerId != mjPlayer.getPlayerId())
                playerIds.add(playerId);
        }
        sender.send(Packet.build(CommandEnum.Command.NOTIFY_MEMBER_LEAVE_ROOM, builder.build().toByteArray()), playerIds);

        if(RoomType.FRIEND_ROOM != roomType) {
            // TODO 游戏过程中主动退出，则启动AI替代
            // 游戏暂停
            status = MJStatus.PREPARE;
        }
    }

    /**
     * 解散房间
     * @param playId 操作者id
     */
    public void dissolvedRoom(long playId) throws GameException {
        if(playId != roomOwnerId) {
            throw new GameException(LanguageConfig.getText(LanguageId.YOU_NO_AUTHORITY_DISSOLVED_ROOM));
        }

        List<Long> playerIds = new ArrayList<>();
        for(Long playerId : seatMap.values()) {
            //  玩家退出房间
            RedisManager.I.del(RedisKeyConstants.CHESS_HALL_PLAYER_INFO + playerId);
            if(playerId != roomOwnerId) {
                playerIds.add(playerId);
            }
        }

        // 通知其他成员解散
        PushSender sender = PushSender.get();
        HallModule.DissolvedRoomResp_1007.Builder builder = HallModule.DissolvedRoomResp_1007.newBuilder();
        sender.send(Packet.build(CommandEnum.Command.DISSOLVED_ROOM, builder.build().toByteArray()), playerIds);
    }

    /**
     * 准备游戏
     * @param playerId
     * @throws GameException
     */
    public void readyGame(long playerId) throws GameException {
        MJPlayer mjPlayer = players.get(playerId);
        if(mjPlayer == null) {
            return;
        }
        if(mjPlayer.getPlayerState() == HallModule.PlayerState.READY)
            throw new GameException(LanguageConfig.getText(LanguageId.YOU_HAD_READY));
        mjPlayer.readGame();

        // 广播发起投票
        List<Long> playerIds = new ArrayList<>();
        for(Long otherId : seatMap.values()) {
            if(playerId != otherId) {
                playerIds.add(otherId);
            }
        }
        HallModule.NotifyMemberReadyGame_1013.Builder builder = HallModule.NotifyMemberReadyGame_1013.newBuilder();
        builder.setSeatId(mjPlayer.getSeat());
        PushSender sender = PushSender.get();
        sender.send(Packet.build(CommandEnum.Command.NOTIFY_MEMBER_READY_GAME, builder.build().toByteArray()), playerIds);

        // 判断是否是所有人都准备好
        int count = 0;
        for(MJPlayer tmpPlayer : players.values()) {
            if(tmpPlayer.getPlayerState() == HallModule.PlayerState.READY)
                ++count;
        }
        if(count == 4) {
            for(MJPlayer tmpPlayer : players.values()) {
                tmpPlayer.toPlaying();
            }
            // 游戏开始
            playerIds.add(playerId);
            HallModule.NotifyRoomBegin_1014.Builder notifyRoomBegin = HallModule.NotifyRoomBegin_1014.newBuilder();
            sender.send(Packet.build(CommandEnum.Command.NOTIFY_ROOM_BEGIN, notifyRoomBegin.build().toByteArray()), playerIds);
        }

    }

    /**
     * 解散房间
     * @param playId 操作者id
     */
    public void voteToDissolved(long playId) throws GameException {
        MJPlayer mjPlayer = players.get(playId);
        if(mjPlayer == null) {
            LogUtils.Console.error("Vote To Dissolved MJTable Fail.Not Exist Player Id:{}", playId);
            return;
        }
        // 重置
        resetVote();
        if(voteEndTime != 0 || !voteSeatMap.isEmpty())
            throw new GameException(LanguageConfig.getText(LanguageId.CAN_NOT_VOTE_TO_DISSOLVED_AGAIN));
        // 发起投票
        voteSeatMap.putIfAbsent(mjPlayer.getSeat(), new VoteInfo(mjPlayer.getSeat(), true));
        voteEndTime = System.currentTimeMillis() + 2 * 60 * 1000;

        // 广播发起投票
        List<Long> playerIds = new ArrayList<>();
        for(Long playerId : seatMap.values()) {
            playerIds.add(playerId);
        }
        HallModule.NotifyDissolvedRoomForVote_1009.Builder builder = HallModule.NotifyDissolvedRoomForVote_1009.newBuilder();
        builder.setSponsorSeatId(mjPlayer.getSeat());
        builder.setVoteEndTime(voteEndTime);// 2分钟期限
        PushSender sender = PushSender.get();
        sender.send(Packet.build(CommandEnum.Command.NOTIFY_DISSOLVED_ROOM_FOR_VOTE, builder.build().toByteArray()), playerIds);
    }

    /**
     * 加入一个玩家
     * @param mjPlayer
     * @throws GameException
     */
    public void memberJoin(MJPlayer mjPlayer) throws GameException {
        if(seatMap.size() >= 4) {
            throw new GameException(LanguageConfig.getText(LanguageId.NO_SEAT_IN_ROOM));
        }
        if(seatMap.isEmpty()) {
            this.roomOwnerId = mjPlayer.getPlayerId();
        }

        // 设置位置
        for(int n = 1; n <= 4 ; ++n) {
            if(!seatMap.contains(n)) {
                mjPlayer.setSeat(n);
                break;
            }
        }

        // 通知其他成员
        PushSender sender = PushSender.get();
        HallModule.NotifyMemberJoinRoom_1004.Builder builder = HallModule.NotifyMemberJoinRoom_1004.newBuilder();
        HallModule.RoomMemberInfo.Builder memberInfo = HallModule.RoomMemberInfo.newBuilder();
        memberInfo.setMemberId(mjPlayer.getPlayerId());
        memberInfo.setMemberName(mjPlayer.getName());
        memberInfo.setSeatId(mjPlayer.getSeat());
        memberInfo.setHead(mjPlayer.getHead());
        memberInfo.setGender(mjPlayer.getGender());
        memberInfo.setIp(mjPlayer.getIp());
        memberInfo.setPlayerState(mjPlayer.getPlayerState());
        builder.setMember(memberInfo);
        List<Long> playerIds = new ArrayList<>();
        for(Long playerId : seatMap.values()) {
            playerIds.add(playerId);
        }
        sender.send(Packet.build(CommandEnum.Command.NOTIFY_MEMBER_JOIN_ROOM, builder.build().toByteArray()), playerIds);
        seatMap.putIfAbsent(mjPlayer.getSeat(), mjPlayer.getPlayerId());
        players.putIfAbsent(mjPlayer.getPlayerId(), mjPlayer);

        // TODO 如果是重登，则接替现有的牌局去玩
    }

    public int getLastMoPai() {
        return lastMoPai;
    }

    public void setLastMoPai(int lastMoPai) {
        this.lastMoPai = lastMoPai;
    }

    public int getLastOutPai() {
        return lastOutPai;
    }

    public void setLastOutPai(int lastOutPai) {
        this.lastOutPai = lastOutPai;
    }

    public int getLastSeat() {
        return lastSeat;
    }

    public void setLastSeat(int lastSeat) {
        this.lastSeat = lastSeat;
    }

    public MJStatus getStatus() {
        return status;
    }

    public void setStatus(MJStatus status) {
        this.status = status;
    }
    
    public int getAction() {
        return action;
    }

    public void setAction(int action) {
        this.action = action;
    }

    public int getNextSeat() {
        return nextSeat;
    }

    public void setNextSeat(int nextSeat) {
        this.nextSeat = nextSeat;
    }

    public int getBankerSeat() {
        return bankerSeat;
    }

    public void setBankerSeat(int bankerSeat) {
        this.bankerSeat = bankerSeat;
    }

    public int getDiceOneNum() {
        return diceOneNum;
    }

    public void setDiceOneNum(int diceOneNum) {
        this.diceOneNum = diceOneNum;
    }

    public int getDiceTwoNum() {
        return diceTwoNum;
    }

    public void setDiceTwoNum(int diceTwoNum) {
        this.diceTwoNum = diceTwoNum;
    }


    public long getActionTime1() {
        return actionTime1;
    }

    public void setActionTime1(long actionTime1) {
        this.actionTime1 = actionTime1;
    }

    public long getActionTime2() {
        return actionTime2;
    }

    public void setActionTime2(long actionTime2) {
        this.actionTime2 = actionTime2;
    }

    public List<Integer> getPlayTypeList() {
        return playTypeList;
    }

    public void setPlayTypeList(List<Integer> playTypeList) {
        this.playTypeList = playTypeList;
    }

    public List<Integer> getExtraFanList() {
        return extraFanList;
    }

    public void setExtraFanList(List<Integer> extraFanList) {
        this.extraFanList = extraFanList;
    }

    public List<Integer> getTablePais() {
        return tablePais;
    }

    public void setTablePais(List<Integer> tablePais) {
        this.tablePais = tablePais;
    }

    
    public ConcurrentHashMap<Long, MJPlayer> getPlayers() {
        return players;
    }

    public void setPlayers(ConcurrentHashMap<Long, MJPlayer> players) {
        this.players = players;
    }

    public ConcurrentHashMap<Integer, Long> getSeatMap() {
        return seatMap;
    }

    public void setSeatMap(ConcurrentHashMap<Integer, Long> seatMap) {
        this.seatMap = seatMap;
    }

    public long getVoteEndTime() {
        return voteEndTime;
    }

    public PayType getPayType() {
        return payType;
    }

    public int getBaseScore() {
        return baseScore;
    }

    public int getCeilingScore() {
        return ceilingScore;
    }

    public ConcurrentHashMap<Integer, List<OptionsType>> getCanOperateMap() {
        return canOperateMap;
    }

    public void setCanOperateMap(ConcurrentHashMap<Integer, List<OptionsType>> canOperateMap) {
        this.canOperateMap = canOperateMap;
    }

    public ConcurrentHashMap<Integer, OptionsType> getOperateMap() {
        return operateMap;
    }

    public void setOperateMap(ConcurrentHashMap<Integer, OptionsType> operateMap) {
        this.operateMap = operateMap;
    }

    /**
     * 牌桌清除方法
     */
    public void tableClear(){
        
    }
    
    /**
     * 移除某一张牌
     * @param pai
     */
    public boolean removePai(Integer pai) {
        boolean flag = false;
        if (getTablePais().contains(pai)) {
            getTablePais().remove(pai);
            flag = true;
        }
        return flag;
    }

    @Override
    public boolean isOver() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void init() {
        // TODO Auto-generated method stub
        
    }

    
    /**
     * 根据玩家座位号拿到玩家对象
     * @param seat
     * @return
     */
    public MJPlayer getPlayerBySeat(int seat){
        for(MJPlayer mahjongPlayer : getPlayers().values()){
            if(seat == mahjongPlayer.getSeat()){
                return mahjongPlayer;
            }
        }
        return null;
    }

    /**
     * 根据玩家id获取玩家对象
     * @param playerId 玩家id
     * @return
     */
    public MJPlayer getPlayerByPlayerId(long playerId){
        for(MJPlayer mahjongPlayer : getPlayers().values()){
            if(mahjongPlayer.getPlayerId() == playerId){
                return mahjongPlayer;
            }
        }
        return null;
    }

    @Override
    public Collection<Long> getAllPlayerIds() {
        List<Long> allPlayerIds = new ArrayList<>();
        for(long playerId : getPlayers().keySet()){
            allPlayerIds.add(playerId);
        }
        return allPlayerIds;
    }
    
    
}
